package com.dragon.english_practice_back.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dragon.english_practice_back.enums.RedisPrefixEnum;
import com.dragon.english_practice_back.mapper.GrammarsMapper;
import com.dragon.english_practice_back.service.AsyncRedisUpdateService;
import com.dragon.english_practice_back.service.GrammarsScheduleService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
@EnableScheduling
@EnableAsync
public class GrammarsScheduleServiceImpl implements GrammarsScheduleService {
    private static int scheduleGrammars = 30;
    GrammarsMapper grammarsMapper;
    RedisTemplate<String, Map<String, Object>> redisTemplate;
    AsyncRedisUpdateService asyncRedisUpdateService;

    @PostConstruct
    public void init(){
        log.info("初始化GrammarsScheduleServiceImpl");
        Long count = grammarsMapper.selectCount(new QueryWrapper<>());
        log.info("grammar表中共有{}条数据",count);
        if (count != null){
            scheduleGrammars = count.intValue();
        }
    }

    public GrammarsScheduleServiceImpl(GrammarsMapper grammarsMapper,
                                       RedisTemplate<String, Map<String, Object>> redisTemplate,
                                       AsyncRedisUpdateService asyncRedisUpdateService) {
        this.grammarsMapper = grammarsMapper;
        this.redisTemplate = redisTemplate;
        this.asyncRedisUpdateService = asyncRedisUpdateService;
    }

    @Override
    public List<Map<String, Object>> getGrammarsSchedule(int id) {
        return getRandomGrammars(id, -1);
    }

    @Override
    public List<Map<String, Object>> getRandomGrammars(int id, int num) {
        // 检查redis中是否有该用户的语法计划
        long redisListSize = redisTemplate.opsForList().size(
                RedisPrefixEnum.GRAMMAR_SCHEDULE.getPrefix() + id
        );
        if (redisListSize == 0) {
            // 如果redis中没有该用户的语法计划，则从数据库中获取
            List<Map<String, Object>> grammars = getGrammarsWithProficiency(id);
            // 加入redis
            redisTemplate.opsForList().rightPushAll(
                    RedisPrefixEnum.GRAMMAR_SCHEDULE.getPrefix() + id,
                    grammars
            );
        }
        // 从redis中获取语法
        List<Map<String, Object>> grammars = redisTemplate.opsForList().range(
                RedisPrefixEnum.GRAMMAR_SCHEDULE.getPrefix() + id,
                0,
                getMaxGrammarsSize() - 1
        );
        // 随机获取num个语法
        if (num > 0 && num < grammars.size()) {
            return randomSample(grammars, num);
        }
        else return grammars;
    }

    @Override
    @Async
    public void updateGrammarsProficiency(int userId, List<Integer> grammarIds, Boolean isCorrect) {
        // 检查grammarIds
        if (grammarIds == null || grammarIds.isEmpty()) {
            return ;
        }
        // 获取用户的语法计划
        List<Map<String, Object>> grammars = getGrammarsWithProficiency(userId);
        if (grammars == null || grammars.isEmpty()) {
            return ;
        }
        // 获取grammar的下标
        List<Integer> indices = new ArrayList<>();
        for (int i = 0; i < grammars.size(); i++) {
            if (grammarIds.contains((Integer) grammars.get(i).get("id"))) {
                indices.add(i);
            }
        }
        if (indices.isEmpty()) {
            return ;
        }

        // 更新语法的熟练度
        asyncRedisUpdateService.updateProficiency(
                redisTemplate,
                RedisPrefixEnum.GRAMMAR_SCHEDULE.getPrefix() + userId,
                RedisPrefixEnum.GRAMMAR_SCHEDULE_LOCK.getPrefix(),
                indices,
                "proficiency",
                isCorrect? 5:-5
        );

        return ;
    }

    @Override
    public int getMaxGrammarsSize() {
        return scheduleGrammars;
    }

    @Override
    @Scheduled(fixedRate = 60 * 60 * 1000)
    public void updateToDatabase() {
        log.info("开始更新语法计划");
        // 获取keys
        Set<String> keys = redisTemplate.keys(
                RedisPrefixEnum.GRAMMAR_SCHEDULE.getPrefix() + "*"
        );
        // 遍历keys
        for (String key : keys) {
            // 从key中获取用户id
            String userId = key.replace(RedisPrefixEnum.GRAMMAR_SCHEDULE.getPrefix(), "");
            // 获取用户的语法计划
            List<Map<String, Object>> grammars = redisTemplate.opsForList().range(
                    key,
                    0,
                    getMaxGrammarsSize() - 1
            );
            // 批量更新数据库
            // 构造map("userId": "id", "grammarId", "proficiency": "proficiency")
            List<Map<String, Object>> list = new ArrayList<>();
            for (Map<String, Object> grammar : grammars) {
                Map<String, Object> map = new HashMap<>();
                map.put("userId", userId);
                map.put("grammarId", grammar.get("id"));
                map.put("proficiency", grammar.get("proficiency"));
                list.add(map);
            }
            // 批量更新
            if (!list.isEmpty()) {
                long updated = grammarsMapper.batchUpdateProficiency(list);
                log.info("更新了userId: {} 的语法熟练度, 更新条数: {}", userId, updated);
            }

        }
        log.info("语法计划更新完成");
    }

    // 从数据库中获取用户附带熟练度的语法表
    private List<Map<String, Object>> getGrammarsWithProficiency(int userId){
        return grammarsMapper.getGrammarsWithProficiency(0, getMaxGrammarsSize(), userId, "id", "asc");
    }

    // 从列表中随机获取N个不重复元素
    private static <T> List<T> randomSample(List<T> list, int n) {
        // 参数校验
        if (n < 0) {
            return new ArrayList<>();
        }
        // 如果n大于列表大小，则返回整个列表
        if (n > list.size()) {
            return new ArrayList<>(list);
        }

        // 复制原列表以避免修改原数据
        List<T> copy = new ArrayList<>(list);
        // 打乱列表顺序
        Collections.shuffle(copy);
        // 返回前N个元素的新列表
        return new ArrayList<>(copy.subList(0, n));
    }
}
